﻿#include "DmMainAfx.h"
#include "DMDPIHelper.h"

namespace DM
{
	DMDPIHelper::DMDPIHelper()
		: m_fInitialized(false)
		, m_bDPIAdaptFlag(false)
		, m_dpiX(96)
		, m_dpiY(96)
		, m_realdpiX(96)
		, m_realdpiY(96)
		, m_fScaleX(1.0f)
		, m_fScaleY(1.0f)
	{
	}

	void DMDPIHelper::EnableHIDPI(bool enable /*= true*/)
	{
		typedef enum _PROCESS_DPI_AWARENESS 
		{
			PROCESS_DPI_UNAWARE = 0,
			PROCESS_SYSTEM_DPI_AWARE = 1,
			PROCESS_PER_MONITOR_DPI_AWARE = 2
		} PROCESS_DPI_AWARENESS;

		typedef HANDLE DPI_AWARENESS_CONTEXT;
		constexpr DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNAWARE
			= reinterpret_cast<DPI_AWARENESS_CONTEXT>(-1);
		constexpr DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_SYSTEM_AWARE
			= reinterpret_cast<DPI_AWARENESS_CONTEXT>(-2);
		constexpr DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE
			= reinterpret_cast<DPI_AWARENESS_CONTEXT>(-3);
		constexpr DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
			= reinterpret_cast<DPI_AWARENESS_CONTEXT>(-4);
		constexpr DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED
			= reinterpret_cast<DPI_AWARENESS_CONTEXT>(-5);

		typedef BOOL(WINAPI* PSetProcessDPIAware)(VOID);
		typedef HRESULT(WINAPI* PSetProcessDpiAwareness)(_In_ PROCESS_DPI_AWARENESS);
		typedef BOOL(WINAPI* PSetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);

		HMODULE hUser32 = ::GetModuleHandleW(L"User32.dll");
		HMODULE hShcore = ::LoadLibraryW(L"Shcore.dll");

		do 
		{
			PSetProcessDPIAware     SetProcessDPIAware = NULL;  /* Windows Vista */
			PSetProcessDpiAwareness SetProcessDpiAwareness = NULL;  /* Windows 8.1 */
			PSetProcessDpiAwarenessContext SetProcessDpiAwarenessContext = NULL; /* Windows 10 */

			if (hShcore != NULL)
			{
				SetProcessDpiAwareness = (PSetProcessDpiAwareness)
					::GetProcAddress(hShcore, "SetProcessDpiAwareness");
			}

			if (hUser32 != NULL)
			{
				SetProcessDPIAware = (PSetProcessDPIAware)
					::GetProcAddress(hUser32, "SetProcessDPIAware");
				SetProcessDpiAwarenessContext = (PSetProcessDpiAwarenessContext)
					::GetProcAddress(hUser32, "SetProcessDpiAwarenessContext");
			}

			if (SetProcessDpiAwarenessContext != NULL)
			{
				if (enable)
				{
					SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
					printf_s(("enable DPI (Win10)\n"));
				}
				else
				{
					SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
					printf_s(("disable DPI (Win10)\n"));
				}
			}
			else if (SetProcessDpiAwareness != NULL)
			{
				if (enable)
				{
					SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);
					printf_s(("enable DPI (Win8.1)\n"));
				}
				else
				{
					SetProcessDpiAwareness(PROCESS_DPI_UNAWARE);
					printf_s(("disable DPI (Win8.1)\n"));
				}
			}
			else if (SetProcessDPIAware != NULL)
			{
				if (enable)
				{
					SetProcessDPIAware();
					printf_s(("enable DPI(Normal)\n"));
				}
			}
		} while (FALSE);

		if (hShcore != NULL)
		{
			::FreeLibrary(hShcore);
			hShcore = NULL;
		}
	}

	void DMDPIHelper::SetDPIAdaptFlag(bool bDPIAdaptFlag, bool bEnableHIDPI /*= true*/)
	{
		m_bDPIAdaptFlag = bDPIAdaptFlag;
		if (bEnableHIDPI)
		{
			EnableHIDPI(bEnableHIDPI);
		}
		if (!m_bDPIAdaptFlag)
		{
			return;
		}

		_Init();
	}

	bool DMDPIHelper::GetDPIAdaptFlag()
	{
		return m_bDPIAdaptFlag;
	}

	int DMDPIHelper::GetDPIX()
	{
		return m_dpiX;
	}

	int DMDPIHelper::GetDPIY()
	{
		return m_dpiY;
	}

	float DMDPIHelper::GetDPIScaleX()
	{
		return m_fScaleX;
	}

	float DMDPIHelper::GetDPIScaleY()
	{
		return m_fScaleY;
	}

	int DMDPIHelper::ScaledScreenWidth()
	{
		return _ScaledSystemMetricX(SM_CXSCREEN);
	}

	int DMDPIHelper::ScaledScreenHeight()
	{
		return _ScaledSystemMetricY(SM_CYSCREEN);
	}

	void DMDPIHelper::_Init()
	{
		if (!m_fInitialized)
		{
			HDC hdc = GetDC(NULL);
			if (hdc)
			{
				m_realdpiX = GetDeviceCaps(hdc, LOGPIXELSX);
				m_realdpiY = GetDeviceCaps(hdc, LOGPIXELSY);
				if (GetDPIAdaptFlag())
				{
					// This will correspond to the DPI setting
					// With all Windows OS's to date the X and Y DPI will be identical
					m_dpiX = m_realdpiX;
					m_dpiY = m_realdpiY;
				}

				LOG_INFO("m_dpiX is %d, m_realdpiX is %d, m_bDPIAdaptFlag is %d\n", m_dpiX, m_realdpiX, m_bDPIAdaptFlag);
				m_fScaleX = m_dpiX / 96.0f;
				m_fScaleY = m_dpiY / 96.0f;
				ReleaseDC(NULL, hdc);
			}
			m_fInitialized = true;
		}
	}

	int DMDPIHelper::_ScaledSystemMetricX(int nIndex)
	{
		return MulDiv(GetSystemMetrics(nIndex), 96, m_dpiX);
	}

	int DMDPIHelper::_ScaledSystemMetricY(int nIndex)
	{
		return MulDiv(GetSystemMetrics(nIndex), 96, m_dpiY);
	}

	bool DMDPIHelper::IsNeededScale()
	{
		if (m_dpiX != 96 || m_dpiY != 96)
		{
			return true;
		}
		return false;
	}

	int DMDPIHelper::GetRealDPIX()
	{
		return m_realdpiX;
	}

	int DMDPIHelper::GetRealDPIY()
	{
		return m_realdpiY;
	}
}
